﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using System.Web.Security;
using System.Text;
using MasoQna.Models;
using MasoQna.Infrastructure.Auth;

namespace MasoQna.Controllers
{
    public class LoginController : Controller
    {
        public ActionResult Index()
        {
            if (Request.IsAuthenticated)
            {
                return View("Index_Error_AlreadyLoggedIn");
            }

            if (Request.Cookies["serviceType"] == null) //첫 접근
            {
                Session.Remove("providedUserId");
                Session.Remove("serviceType");
                RemoveCookie();
                return View(auth.GetImagePaths());
            }
            else //인증 위해 접근
            {
                int type = int.Parse(Request.Cookies["serviceType"].Value);
                RemoveCookie();

                string providedUserId = null;
                bool authSuccess = false;

                try
                {
                    authSuccess = auth.FinishAuthentication(type, out providedUserId);
                }
                catch (Exception)
                {
                    authSuccess = false;
                }

                if (authSuccess == true)
                {
                    if (string.IsNullOrEmpty(providedUserId))
                    {
                        RemoveCookie();
                        return View(auth.GetImagePaths());
                    }

                    int userId = userRepo.GetUserId(type, providedUserId);

                    if (userId != 0) // 기존 사용자
                    {
                        return ApproveLogin(userId);
                    }
                    else // 신규 사용자
                    {
                        Session["providedUserId"] = providedUserId;
                        Session["serviceType"] = type;
                        return RedirectToAction("CreateAccount");
                    }
                }
                else
                {
                    return View("Index_Error_AuthenticationError");
                }
            }
        }

        private void RemoveCookie()
        {
            Response.Cookies.Add(new HttpCookie("serviceType")
            {
                Value = null,
                Expires = DateTime.Now.AddDays(-1)
            });
        }

        private ActionResult ApproveLogin(int userId)
        {
            bool rememberMe = false;

            if (Session["rememberMe"] != null)
            {
                rememberMe = (bool)Session["rememberMe"];
            }

            auth.ApproveLogin(userId.ToString(), rememberMe);
            Session.Remove("rememberMe");

            string returnUrl = (string)Session["returnUrl"] ?? "/";

            Session.Remove("returnUrl");
            return Redirect(returnUrl);
        }

        [HttpPost]
        public ActionResult Index(int serviceType, bool rememberMe, string returnUrl = "")
        {
            returnUrl = HttpUtility.UrlDecode(returnUrl);
            Session["returnUrl"] = ValidateReturnUrl(returnUrl) ? returnUrl : "/";

            if (auth.IsValidServiceType(serviceType) == false)
            {
                RemoveCookie();
                return View("Index_Error_AuthenticationError");
            }

            try
            {
                Session["rememberMe"] = rememberMe;

                Response.Cookies.Add(new HttpCookie("serviceType")
                {
                    Value = serviceType.ToString(),
                    Expires = DateTime.Now.AddMinutes(20),
                    HttpOnly = true
                });

                auth.StartAuthentication(serviceType);
            }
            catch (Exception)
            {
                RemoveCookie();
                return View("Index_Error_AuthenticationError");
            }

            return new EmptyResult();
        }

        public bool ValidateReturnUrl(string url)
        {
            return Url.IsLocalUrl(url) && url.Length > 1 && url.StartsWith("/")
                        && !url.StartsWith("//") && !url.StartsWith("/\\");
        }

        private IAuthProvider auth = new AuthProvider();
        private IUserRepository userRepo = new UserRepository();


        //
        // GET: /Account/LogOn

        public ActionResult LogOn()
        {
            return View();
        }

        public ViewResult CreateAccount()
        {
            if (Session["providedUserId"] == null || Session["serviceType"] == null)
            {
                return View("CreateAccount_Error_NoAuthInfo");
            }
            //*/

            int count = (from u in userRepo.Users select u.Id).Count();
            string defaultName = "사용자" + (count + 1);

            User summary = new User()
            {
                Name = defaultName,
                Reputation = initialReputation,
            };
            return View(summary);
        }

        [HttpPost]
        [ValidateInput(false)]
        public ActionResult CreateAccount(string nickname)
        {

            if (Session["providedUserId"] == null || Session["serviceType"] == null)
            {
                return View("CreateAccount_Error_TimeOut");
            }
            //*/

            List<string> errors = new List<string>();
            if (IsValidNickname(nickname, errors) == false)
            {
                foreach (string e in errors)
                {
                    ModelState.AddModelError("nickname", e);
                }
            }

            if (ModelState.IsValid == false)
            {
                return View(new User() { Name = nickname, Reputation = initialReputation });
            }

            string providedUserId = Session["providedUserId"] as string;
            int serviceType = (int)Session["serviceType"];

            if (userRepo.GetUserId(serviceType, providedUserId) == 0)
            {
                nickname = HttpUtility.HtmlEncode(nickname);
                int userId = userRepo.AddUser(new User() { Name = nickname, Reputation = initialReputation });
                bool success = userRepo.AddUserMapping(serviceType, providedUserId, userId);

                if (success == false)
                {
                    return View("CreateAccount_Error_UserAlreadyRegistered");
                }

                return ApproveLogin(userId);
            }
            else
            {
                return View("CreateAccount_Error_UserAlreadyRegistered");
            }
        }

        private static int initialReputation = 200;

        private bool IsValidNickname(string name, List<string> errorMessages)
        {
            if (name.Length > 20)
            {
                errorMessages.Add("별명은 20자 이상될 수 없습니다.");
            }

            Dictionary<string, Func<char, bool>> whitelist = new Dictionary<string, Func<char, bool>>();
            whitelist["default"] = c => ((c >= '0' && c <= '9') ||
                (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') ||
                (c == '.') || (c == '-'));
            whitelist["kr"] = c => ((c >= 'ㄱ' && c <= '\u11F9') ||
                (c >= '가' && c <= '힣'));

            string[] langs = "kr".Split(' ');//현재는 한국어만 지원

            List<Func<char, bool>> selectedWhiteList = new List<Func<char, bool>>();
            selectedWhiteList.Add(whitelist["default"]);

            if (langs[0] != "")
            {
                foreach (string lang in langs)
                {
                    selectedWhiteList.Add(whitelist[lang]);
                }
            }

            char[] letters = name.ToCharArray();
            HashSet<char> invalidLetters = new HashSet<char>();

            foreach (char ch in letters)
            {
                bool isValid = false;
                foreach (var f in selectedWhiteList)
                {
                    if (f(ch))
                    {
                        isValid = true;
                    }
                }
                if (isValid == false)
                {
                    invalidLetters.Add(ch);
                }
            }

            if (invalidLetters.Count != 0)
            {
                StringBuilder invalidList = new StringBuilder();
                foreach (char ch in invalidLetters)
                {
                    invalidList.Append(ch);
                    invalidList.Append(' ');
                }
                errorMessages.Add(string.Format("다음 문자들은 사용할 수 없습니다. : {0}", invalidList.ToString()));
            }


            if (errorMessages.Count != 0)
            {
                return false;
            }

            return true;
        }

        public RedirectResult LogOut(string returnUrl)
        {
            auth.LogOut();
            return Redirect(HttpUtility.UrlDecode(returnUrl));
        }
    }
}
